//************
// VARIABLES *
//************

float4x4 m_MatrixWorldViewProj : WORLDVIEWPROJECTION;
float4x4 m_MatrixWorld : WORLD;
float4x4 m_MatrixViewInv : VIEWINVERSE;

float3 m_LightDir:DIRECTION = float3(0.577f, 0.577f, 0.577f);

Texture2D m_TextureDiffuse;
Texture2D m_TextureFurDensity;
Texture2D m_TextureMask;
float m_Gravity = -9.81f;
float m_FurLength;
float m_FurUV;

RasterizerState FrontCulling 
{ 
	CullMode = FRONT;
	MultisampleEnable = true;
	AntialiasedLineEnable = true;
};

RasterizerState NoneCulling 
{ 
	CullMode = NONE; 
};

RasterizerState BackCulling 
{ 
	CullMode = BACK; 
};

BlendState EnableBlending 
{     
	BlendEnable[0] = TRUE;
	SrcBlend = SRC_ALPHA;
    DestBlend = INV_SRC_ALPHA;
};

SamplerState samLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;// of Mirror of Clamp of Border
    AddressV = Wrap;// of Mirror of Clamp of Border
};

DepthStencilState NoDepth
{
	DepthEnable = true;
    DepthWriteMask = 0x00;
    DepthFunc = Less;
    StencilEnable = false;
};

//**********
// STRUCTS *
//**********
struct VS_DATA
{
	float3 Position : POSITION;
	float3 Normal : NORMAL;
    float2 TexCoord : TEXCOORD;
};

struct GS_DATA
{
	float4 Position : SV_POSITION;
	float3 Normal : NORMAL;
	float2 TexCoord : TEXCOORD0;
	float ShadowStrength : FLOAT;
};

//****************
// VERTEX SHADER *
//****************
VS_DATA MainVS(VS_DATA vsData)
{

	return vsData;
}

//*****************
//GEOMETRY SHADER *
//*****************
void CreateVertex(inout TriangleStream<GS_DATA> triStream, float3 pos, float3 normal, float2 texCoord, float shadowStrength)
{
	//Step 1. Create a GS_DATA object
	GS_DATA gOut;

	//Step 2. Transform the position using the WVP Matrix and assign it to (GS_DATA object).Position (Keep in mind: float3 -> float4)
	gOut.Position = mul( float4(pos,1) ,m_MatrixWorldViewProj);

	//Step 3. Transform the normal using the World Matrix and assign it to (GS_DATA object).Normal (Only Rotation, No translation!)
	gOut.Normal = mul(normal, (float3x3)m_MatrixWorld);

	//Step 4. Assign texCoord to (GS_DATA object).TexCoord
	gOut.TexCoord = texCoord;

	gOut.ShadowStrength = shadowStrength;
	
	//Step 5. Append (GS_DATA object) to the TriangleStream parameter (TriangleStream::Append(...))
	triStream.Append( gOut );
}

void CreateFurVertex(inout TriangleStream<GS_DATA> triStream, float4 pos, float3 normal, float2 texCoord, float shadowStrength)
{
	//Step 1. Create a GS_DATA object
	GS_DATA gOut;

	//Step 2. Transform the position using the WVP Matrix and assign it to (GS_DATA object).Position (Keep in mind: float3 -> float4)
	gOut.Position = pos;

	//Step 3. Transform the normal using the World Matrix and assign it to (GS_DATA object).Normal (Only Rotation, No translation!)
	gOut.Normal = mul(normal, (float3x3)m_MatrixWorld);

	//Step 4. Assign texCoord to (GS_DATA object).TexCoord
	gOut.TexCoord = texCoord;

	gOut.ShadowStrength = shadowStrength;
	
	//Step 5. Append (GS_DATA object) to the TriangleStream parameter (TriangleStream::Append(...))
	triStream.Append( gOut );
}

[maxvertexcount(99)]
void FurGenerator(triangle VS_DATA vertices[3], inout TriangleStream<GS_DATA> triStream)
{
	
	//Use these variable names
	float3 v1, v2, v3;
	float3 g = float3(0,m_Gravity,0);
	
	float Stiffness = 0;

	int maxLayers = 33;
	for( int i = 0; i < maxLayers; ++i)
	{
		float length = ((float)i+1) * m_FurLength/maxLayers;
		v1 = vertices[0].Position + vertices[0].Normal * length;
		v2 = vertices[1].Position + vertices[1].Normal * length;
		v3 = vertices[2].Position + vertices[2].Normal * length;
		
		float4 nV1, nV2, nV3;	//Set To WorldPosition
		nV1 = mul( float4(v1,1) ,m_MatrixWorldViewProj);
		nV2 = mul( float4(v2,1) ,m_MatrixWorldViewProj);
		nV3= mul( float4(v3,1) ,m_MatrixWorldViewProj);
		
		//gravity
		float4 h = float4(g,0)*pow(length,2)*i/maxLayers/m_FurLength;
		//h = 0;
		nV1 = nV1 + h;
		nV2 = nV2 + h;
		nV3 = nV3 + h;
		
		float shadowStr = 0.5f + (float)(i+1)/maxLayers;
		CreateFurVertex(triStream, nV1, vertices[0].Normal, vertices[0].TexCoord, shadowStr );
		CreateFurVertex(triStream, nV2, vertices[1].Normal, vertices[1].TexCoord, shadowStr );
		CreateFurVertex(triStream, nV3, vertices[2].Normal, vertices[2].TexCoord, shadowStr );
		triStream.RestartStrip();
	}	
}

[maxvertexcount(3)]
void BaseRenderer(triangle VS_DATA vertices[3], inout TriangleStream<GS_DATA> triStream)
{
	//Create Existing Geometry
	CreateVertex(triStream,vertices[0].Position,vertices[0].Normal,vertices[0].TexCoord, 0.5f);
	CreateVertex(triStream,vertices[1].Position,vertices[1].Normal,vertices[1].TexCoord, 0.5f);
	CreateVertex(triStream,vertices[2].Position,vertices[2].Normal,vertices[2].TexCoord, 0.5f);		
}


//***************
// PIXEL SHADER *
//***************
float4 MainPS(GS_DATA input) : SV_TARGET 
{
	input.Normal = -normalize(input.Normal);
	float alpha = m_TextureDiffuse.Sample(samLinear,input.TexCoord).a;
	float3 color = m_TextureDiffuse.Sample( samLinear,input.TexCoord ).rgb;
	float mask = m_TextureMask.Sample( samLinear,input.TexCoord ).r/3 + m_TextureMask.Sample( samLinear,input.TexCoord ).g/3 + m_TextureMask.Sample( samLinear,input.TexCoord ).b/3;
	color = color * (  2 - mask  ) * input.ShadowStrength;
	float s = max(dot(m_LightDir,input.Normal), 0.4f);
	
	return float4(color*s,alpha);
}


float4 FurPS(GS_DATA input) : SV_TARGET 
{
	input.Normal = -normalize(input.Normal);
	float alpha = m_TextureFurDensity.Sample(samLinear,input.TexCoord*m_FurUV).a;
	float mask = m_TextureMask.Sample( samLinear,input.TexCoord ).r/3 + m_TextureMask.Sample( samLinear,input.TexCoord ).g/3 + m_TextureMask.Sample( samLinear,input.TexCoord ).b/3;
	alpha *= mask;
	
	float3 color = m_TextureDiffuse.Sample( samLinear,input.TexCoord ).rgb;//m_TextureFur.Sample( samLinear,input.TexCoord ).rgb;
	color = color * input.ShadowStrength;
	float s = max(dot(m_LightDir,input.Normal), 0.4f);
	
	return float4(color*s,alpha);
}


//*************
// TECHNIQUES *
//*************

technique10 DefaultTechnique 
{
	pass p0 {
		SetRasterizerState(FrontCulling);
		//SetDepthStencilState(NoDepth,0);
		SetBlendState(EnableBlending,float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF);
		SetVertexShader(CompileShader(vs_4_0, MainVS()));
		SetGeometryShader(CompileShader(gs_4_0, BaseRenderer()));
		SetPixelShader(CompileShader(ps_4_0, MainPS()));
	}
	pass p1 {
		SetRasterizerState(FrontCulling);
		SetDepthStencilState(NoDepth,0);
		SetBlendState(EnableBlending,float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF);
		SetVertexShader(CompileShader(vs_4_0, MainVS()));
		SetGeometryShader(CompileShader(gs_4_0, FurGenerator()));
		SetPixelShader(CompileShader(ps_4_0, FurPS()));
	}
}